﻿// (c) Copyright Fernando Cerqueira.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using DevPhone.Controls.LocalizedResources;
using Microsoft.Phone.Controls;
using System.Globalization;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    internal class WindowsBoxTimePicker : WindowsBoxBase
    {

        #region Create / template

        public WindowsBoxTimePicker()
        {
            ButtonType = WindowsBoxButtonType.OkCancel;
            TypeMessage = WindowsBoxTypeMessage.TimePicker;
            DefaultStyleKey = typeof(WindowsBoxTimePicker);
        }

        private void ApplySelectors()
        {
            _primarySelectorPart = GetTemplateChild("PrimarySelector") as LoopingSelector;
            _secondarySelectorPart = GetTemplateChild("SecondarySelector") as LoopingSelector;
            _tertiarySelectorPart = GetTemplateChild("TertiarySelector") as LoopingSelector;

            if (null == _primarySelectorPart)
            {
                throw new InvalidOperationException("Element primary selector not found");
            }
            if (null == _secondarySelectorPart)
            {
                throw new InvalidOperationException("Element secondary selector not found");
            }
            if (null == _tertiarySelectorPart)
            {
                throw new InvalidOperationException("Element tertiary selector not found");
            }

            _primarySelectorPart.DataSource = DateTimeWrapper.CurrentCultureUsesTwentyFourHourClock() ?
                 new TwentyFourHourDataSource(MessageForeColor) :
                 (DataSource)(new TwelveHourDataSource(MessageForeColor));
            _secondarySelectorPart.DataSource = new MinuteDataSource(MessageForeColor);
            _tertiarySelectorPart.DataSource = new AmPmDataSource(MessageForeColor);

            // Hook up to events
            _primarySelectorPart.DataSource.SelectionChanged += OnDataSourceSelectionChanged;
            _secondarySelectorPart.DataSource.SelectionChanged += OnDataSourceSelectionChanged;
            _tertiarySelectorPart.DataSource.SelectionChanged += OnDataSourceSelectionChanged;
            _primarySelectorPart.IsExpandedChanged += OnSelectorIsExpandedChanged;
            _secondarySelectorPart.IsExpandedChanged += OnSelectorIsExpandedChanged;
            _tertiarySelectorPart.IsExpandedChanged += OnSelectorIsExpandedChanged;

            // Hide all selectors
            _primarySelectorPart.Visibility = Visibility.Collapsed;
            _secondarySelectorPart.Visibility = Visibility.Collapsed;
            _tertiarySelectorPart.Visibility = Visibility.Collapsed;

            // Position and reveal the culture-relevant selectors
            var column = 0;
            AllSelectorsWidth = 0.0;
            foreach (var selector in GetSelectorsOrderedByCulturePattern(
                CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern.ToUpperInvariant(),
                new[] { 'H', 'M', 'T' },
                new[] { _primarySelectorPart, _secondarySelectorPart, _tertiarySelectorPart }))
            {
                Grid.SetColumn(selector, column);
                if (selector.Name == "TertiarySelector")
                {
                    if (CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern.Contains("t"))
                    {
                        selector.Visibility = Visibility.Visible;
                        AllSelectorsWidth += 140;
                    }
                    else
                    {
                        var cold = GetTemplateChild("Col" + column) as ColumnDefinition;
                        if (cold != null)
                        {
                            cold.Width = new GridLength(0);
                        }
                    }
                }
                else
                {
                    selector.Visibility = Visibility.Visible;
                    AllSelectorsWidth += 140;
                }
                column++;
            }
            if (column == 2)
            {
                var cold = GetTemplateChild("Col" + column) as ColumnDefinition;
                if (cold != null)
                {
                    cold.Width = new GridLength(0);
                }
            }

            //set initial dateTime
            var wrapper = new DateTimeWrapper(DateTimeSelected.GetValueOrDefault(DateTime.Now), true,MessageForeColor);
            _primarySelectorPart.DataSource.SelectedItem = wrapper;
            _secondarySelectorPart.DataSource.SelectedItem = wrapper;
            _tertiarySelectorPart.DataSource.SelectedItem = wrapper;
        }

        public override void OnApplyTemplate()
        {
            _currentPage = RootGridHost.Parent as PhoneApplicationPage;
            //Handling the “Back” Button or exist page
            if (_currentPage != null)
            {
                _currentPage.Unloaded += PhonePageUnloaded;
                _currentPage.BackKeyPress += PhoneBackKeyPress;
            }
            if (RootGridHost.RowDefinitions.Count > 0)
            {
                Grid.SetRowSpan(this, RootGridHost.RowDefinitions.Count);
            }
            if (RootGridHost.ColumnDefinitions.Count > 0)
            {
                Grid.SetColumnSpan(this, RootGridHost.ColumnDefinitions.Count);
            }
            if (Buttons.Count == 0)
            {
                Buttons = new List<WindowsBoxButton> { new WindowsBoxButton(ControlResources.WindowsBoxService_Ok, (int)WindowsBoxButtonResult.ButtonOk), new WindowsBoxButton(ControlResources.WindowsBoxService_Cancel, (int)WindowsBoxButtonResult.ButtonCancel) };
            }

            _buttonClick = WindowsBoxButtonResult.None;
            _buttonValueClick = null;

            base.OnApplyTemplate();

            HandlingStoryboardClose();

            //apply static values
            OverlayBrush = WindowsBoxService.DefaultOverlayBrush;

            CornerRadiusTop = WindowsBoxService.DefaultCornerRadiusTop;
            CornerRadiusBottom = WindowsBoxService.DefaultCornerRadiusBottom;

            ThicknessTitle = WindowsBoxService.DefaultThicknessTitle;
            ThicknessMessage = WindowsBoxService.DefaultThicknessMessage;
            ThicknessButtons = WindowsBoxService.DefaultThicknessButtons;

            MessageBorderBrush = WindowsBoxService.DefaultBorderBrush;

            ApplyColors();

            ApplyButtons();

            ApplySelectors();

            Dispatcher.BeginInvoke(() => VisualStateManager.GoToState(this, "Open", true));
            IsOpen = true;
            OnOpened(new EventArgs());
        }

        private void OnDataSourceSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Push the selected item to all selectors
            var dataSource = (DataSource)sender;
            _primarySelectorPart.DataSource.SelectedItem = dataSource.SelectedItem;
            _secondarySelectorPart.DataSource.SelectedItem = dataSource.SelectedItem;
            _tertiarySelectorPart.DataSource.SelectedItem = dataSource.SelectedItem;

            var newdate = ((DateTimeWrapper)dataSource.SelectedItem).DateTime;
            if (!DateTimeSelected.HasValue)
            {
                DateTimeSelected = newdate;
            }
            else if (DateTimeSelected.Value != newdate)
            {
                DateTimeSelected = newdate;
            }
        }

        private void OnSelectorIsExpandedChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                // Ensure that only one selector is expanded at a time
                _primarySelectorPart.IsExpanded = (sender == _primarySelectorPart);
                _secondarySelectorPart.IsExpanded = (sender == _secondarySelectorPart);
                _tertiarySelectorPart.IsExpanded = (sender == _tertiarySelectorPart);
            }
        }

        private static IEnumerable<LoopingSelector> GetSelectorsOrderedByCulturePattern(string pattern, char[] patternCharacters, LoopingSelector[] selectors)
        {
            if (null == pattern)
            {
                throw new ArgumentNullException("pattern");
            }
            if (null == patternCharacters)
            {
                throw new ArgumentNullException("patternCharacters");
            }
            if (null == selectors)
            {
                throw new ArgumentNullException("selectors");
            }
            if (patternCharacters.Length != selectors.Length)
            {
                throw new ArgumentException("Arrays must contain the same number of elements.");
            }

            // Create a list of index and selector pairs
            var pairs = new List<Tuple<int, LoopingSelector>>(patternCharacters.Length);
            pairs.AddRange(patternCharacters.Select((t, i) => new Tuple<int, LoopingSelector>(pattern.IndexOf(t), selectors[i])));

            // Return the corresponding selectors in order
            return pairs.Where(p => -1 != p.Item1).OrderBy(p => p.Item1).Select(p => p.Item2).Where(s => null != s);
        }

        #endregion

        #region internal properties

        internal DateTime? DateTimeSelected { get; set; }

        #endregion

        #region private fields

        private StackPanel _stackButtonseContent;
        private PhoneApplicationPage _currentPage;
        private Storyboard _closeStoryboard;
        private WindowsBoxButtonResult _buttonClick;
        private DateTimeWrapper _buttonValueClick;

        private LoopingSelector _primarySelectorPart;
        private LoopingSelector _secondarySelectorPart;
        private LoopingSelector _tertiarySelectorPart;



        #endregion

        #region events /callback

        private void CloseStoryboardCompleted(object sender, EventArgs e)
        {
            var arg = new WindowsBoxResultEventArgs(_buttonClick, _buttonValueClick);
            OnClosed(arg);
            CleanUp();
            IsOpen = false;
        }

        private void PhoneBackKeyPress(object sender, CancelEventArgs e)
        {
            if (IsOpen)
            {
                e.Cancel = true;
                _buttonClick = WindowsBoxButtonResult.BackKeyPress;
                _buttonValueClick = null;
                if (_closeStoryboard == null)
                {
                    CloseStoryboardCompleted(this, new EventArgs());
                }
                else
                {
                    VisualStateManager.GoToState(this, "Closed", true);
                }
                WindowsBoxService.IsOpen = false;
            }
            else
            {
                CleanUp();
            }
        }

        private void PhonePageUnloaded(object sender, RoutedEventArgs e)
        {
            if (IsOpen)
            {
                _buttonClick = WindowsBoxButtonResult.BackKeyPress;
                _buttonValueClick = null;
                if (_closeStoryboard == null)
                {
                    CloseStoryboardCompleted(this, new EventArgs());
                }
                else
                {
                    VisualStateManager.GoToState(this, "Closed", true);
                }
                WindowsBoxService.IsOpen = false;
            }
            else
            {
                CleanUp();
            }
        }

        private void ButtomClick(object sender, RoutedEventArgs e)
        {
            var bnt = (FrameworkElement)sender;
            Close((WindowsBoxButtonResult)(int)bnt.Tag);
        }

        #endregion

        #region private methods

        private void HandlingStoryboardClose()
        {
            _closeStoryboard = null;
            var rootTemplate = GetTemplateChild("Root") as FrameworkElement;
            if (rootTemplate != null)
            {
                var visualStateGroups = VisualStateManager.GetVisualStateGroups(rootTemplate);
                if (visualStateGroups.Count > 0)
                {
                    var list = (from VisualStateGroup visualStateGroup in visualStateGroups where visualStateGroup.Name == "WindowStates" select visualStateGroup.States).FirstOrDefault();
                    if (list != null)
                    {
                        foreach (var visualState in list.Cast<VisualState>().Where(visualState => visualState.Name == "Closed"))
                        {
                            _closeStoryboard = visualState.Storyboard;
                            break;
                        }
                    }
                }
            }
            if (_closeStoryboard != null)
            {
                _closeStoryboard.Completed += CloseStoryboardCompleted;
            }
        }

        private void ApplyColors()
        {
            switch (TypeMessage)
            {
                case WindowsBoxTypeMessage.UserType:
                    TitleForeColor = WindowsBoxService.DefaultTitleForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultTitleBackgroundBrush;
                    break;
                case WindowsBoxTypeMessage.ErrorType:
                    TitleForeColor = WindowsBoxService.DefaultErrorForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultErrorBackgroundBrush;
                    break;
                case WindowsBoxTypeMessage.WarningType:
                    TitleForeColor = WindowsBoxService.DefaultWarningForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultWarningBackgroundBrush;
                    break;
                case WindowsBoxTypeMessage.InformationType:
                    TitleForeColor = WindowsBoxService.DefaultInformationForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultInformationBackgroundBrush;
                    break;
                case WindowsBoxTypeMessage.QuestionType:
                    TitleForeColor = WindowsBoxService.DefaultQuestionForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultQuestionBackgroundBrush;
                    break;
                default:
                    TitleForeColor = WindowsBoxService.DefaultTitleForegroundBrush;
                    TitleBackColor = WindowsBoxService.DefaultTitleBackgroundBrush;
                    break;
            }

            MessageForeColor = WindowsBoxService.DefaultContentForegroundBrush;
            MessageBackColor = WindowsBoxService.DefaultContentBackgroundBrush;

        }

        private void ApplyButtons()
        {
            _stackButtonseContent = GetTemplateChild("ButtonseContent") as StackPanel;
            if (_stackButtonseContent != null)
            {
                foreach (var bnt in Buttons
                    .Select(item => new Button
                    {
                        Content = item.Text,
                        Tag = item.Value,
                        Foreground = WindowsBoxService.DefaultButtonForegroundBrush,
                        Background = WindowsBoxService.DefaultButtonBackgroundBrush
                    }))
                {
                    bnt.Click += ButtomClick;
                    _stackButtonseContent.Children.Add(bnt);
                }
            }
        }

        private void OnOpened(EventArgs e)
        {
            var handler = ActionOpen;
            if (handler != null) handler.Invoke(e);
        }

        private void OnClosed(WindowsBoxResultEventArgs e)
        {
            var handler = ActionClosed;
            if (handler != null) handler.Invoke(e);
        }

        internal override void CleanUp()
        {
            if (IsOpen)
            {
                UnHanddlerEvents();
                if (RootGridHost != null)
                {
                    RootGridHost.Children.Remove(RootGridHost.GetFirstLogicalChildByType<WindowsBoxTimePicker>(false));
                }
            }
            base.CleanUp();

            Buttons.Clear();
            ActionClosed = null;
            ActionOpen = null;

            _buttonClick = WindowsBoxButtonResult.None;
            _buttonValueClick = null;
            _stackButtonseContent = null;
            _currentPage = null;

            _primarySelectorPart = null;
            _secondarySelectorPart = null;
            _tertiarySelectorPart = null;

        }

        private void UnHanddlerEvents()
        {
            if (IsOpen)
            {
                if (_closeStoryboard != null)
                {
                    _closeStoryboard.Completed -= CloseStoryboardCompleted;
                }

                _currentPage.Unloaded -= PhonePageUnloaded;
                _currentPage.BackKeyPress -= PhoneBackKeyPress;

                if (_stackButtonseContent != null && _stackButtonseContent.Children.Count > 0)
                {
                    foreach (var item in _stackButtonseContent.GetVisualChildren().OfType<Button>())
                    {
                        item.Click -= ButtomClick;
                    }
                }
            }
        }

        #endregion

        #region Internal Methods

        internal void Close()
        {
            if (IsOpen)
            {
                _buttonClick = WindowsBoxButtonResult.None;
                _buttonValueClick = null;
                if (_closeStoryboard == null)
                {
                    CloseStoryboardCompleted(this, new EventArgs());
                }
                else
                {
                    VisualStateManager.GoToState(this, "Closed", true);
                }
                WindowsBoxService.IsOpen = false;
            }
            else
            {
                CleanUp();
            }
        }

        internal void Close(WindowsBoxButtonResult buttonResult)
        {
            if (IsOpen)
            {
                _buttonClick = buttonResult;
                _buttonValueClick = null;
                if (DateTimeSelected.HasValue && buttonResult == WindowsBoxButtonResult.ButtonOk)
                {
                    _buttonValueClick = new DateTimeWrapper(DateTimeSelected.Value);
                }
                if (_closeStoryboard == null)
                {
                    CloseStoryboardCompleted(this, new EventArgs());
                }
                else
                {
                    VisualStateManager.GoToState(this, "Closed", true);
                }
                WindowsBoxService.IsOpen = false;
            }
            else
            {
                CleanUp();
            }
        }

        #endregion

    }

}
